home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #1 / Ham Radio 2000.iso / ham2000 / tcp_ip / wnos / wn941101 / popserv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-10  |  13.0 KB  |  615 lines

  1. /* POP Server state machine - see RFC 937
  2.  *
  3.  *  also see other credits in popcli.c
  4.  *  10/89 Mike Stockett wa7dyx
  5.  *  Modified 5/27/90 by Allen Gwinn, N5CKP, for later NOS releases.
  6.  *  Added to NOS by PA0GRI 2/6/90 (and linted into "standard" C)
  7.  *    some code streaming - DB3FL.911111
  8.  */
  9.  
  10. #include <stdio.h>
  11. #include <time.h>
  12. #include <sys/stat.h>
  13. #ifdef UNIX
  14. #include <sys/types.h>
  15. #endif
  16. #if    defined(__STDC__) || defined(__TURBOC__)
  17. #include <stdarg.h>
  18. #endif
  19. #include <ctype.h>
  20. #include <setjmp.h>
  21. #include "global.h"
  22. #include "config.h"
  23. #ifdef POP
  24. #include "mbuf.h"
  25. #include "cmdparse.h"
  26. #include "socket.h"
  27. #include "proc.h"
  28. #include "files.h"
  29. #include "pop.h"
  30.  
  31. #undef LOG
  32. #define    BITS_PER_WORD        16
  33. #define isSOM(x)        ((strncmp(x,"From ",5) == 0))
  34.  
  35. static int Spop = -1; /* prototype socket for service */
  36.  
  37. /* Command string specifications */
  38. static char    ackd_cmd[]     = "ACKD",
  39.             acks_cmd[]     = "ACKS",
  40. #ifdef POP_FOLDERS
  41.             fold_cmd[]     = "FOLD ",
  42. #endif
  43.             login_cmd[] = "HELO ",
  44.             nack_cmd[]     = "NACK",
  45.             quit_cmd[]     = "QUIT",
  46.             read_cmd[]     = "READ",
  47.             retr_cmd[]     = "RETR";
  48.  
  49. static void near
  50. state_error(scb,msg)
  51. struct pop_scb *scb;
  52. char *msg;
  53. {
  54.     usprintf(scb->socket,error_rsp,msg);
  55.     scb->state = DONE;
  56. }
  57.  
  58. static int near
  59. newmail(scb)
  60. struct pop_scb *scb;
  61. {
  62.     char *folder_pathname;
  63.     struct stat folder_stat;
  64.     int s;
  65.  
  66.     folder_pathname = mxallocw(strlen(Mailspool) + strlen(scb->username) + 5);
  67.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  68.     s = stat(folder_pathname,&folder_stat);
  69.     xfree(folder_pathname);
  70.  
  71.     if(s) {
  72.         state_error(scb,"Unable to get old mail folder's status");
  73.         return(FALSE);
  74.     } else {
  75.         return((folder_stat.st_size > scb->folder_file_size) ? TRUE : FALSE);
  76.     }
  77. }
  78.  
  79. static int near
  80. isdeleted(scb,msg_no)
  81. struct pop_scb *scb;
  82. int msg_no;
  83. {
  84.     unsigned int mask = 1, offset;
  85.  
  86.     msg_no--;
  87.     offset = msg_no / BITS_PER_WORD;
  88.     mask <<= msg_no % BITS_PER_WORD;
  89.     return (((scb->msg_status[offset]) & mask) ? TRUE : FALSE);
  90. }
  91.  
  92. static void near
  93. close_folder(scb)
  94. struct pop_scb *scb;
  95. {
  96.     char folder_pathname[64], line[BUF_LEN];
  97.     FILE *fd;
  98.     int deleted = FALSE, msg_no = 0;
  99.     struct stat folder_stat;
  100.  
  101.     if (scb->wf == NULL)
  102.         return;
  103.  
  104.     if (!scb->folder_modified) {
  105.         /* no need to re-write the folder if we have not modified it */
  106.  
  107.         fclose(scb->wf);
  108.         scb->wf = NULL;
  109.  
  110.         xfree((char *)scb->msg_status);
  111.         scb->msg_status = NULL;
  112.         return;
  113.     }
  114.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  115.  
  116.     if (newmail(scb)) {
  117.         /* copy new mail into the work file and save the
  118.            message count for later */
  119.         if ((fd = open_file(folder_pathname,"r",0,1)) == NULLFILE)
  120.             return;
  121.  
  122.         fseek(scb->wf,0,SEEK_END);
  123.         fseek(fd,scb->folder_file_size,SEEK_SET);
  124.         while (!feof(fd)) {
  125.             fgets(line,BUF_LEN,fd);
  126.             fputs(line,scb->wf);
  127.         }
  128.  
  129.         fclose(fd);
  130.     }
  131.     /* now create the updated mail folder */
  132.     if ((fd = open_file(folder_pathname,"w",0,1)) == NULLFILE)
  133.         return;
  134.  
  135.     rewind(scb->wf);
  136.     while (!feof(scb->wf)){
  137.         fgets(line,BUF_LEN,scb->wf);
  138.  
  139.         if (isSOM(line)){
  140.             msg_no++;
  141.             deleted = (msg_no <= scb->folder_len) ?
  142.                 isdeleted(scb,msg_no) : FALSE;
  143.         }
  144.         if (deleted)
  145.             continue;
  146.  
  147.         fputs(line,fd);
  148.     }
  149.  
  150.     fclose(fd);
  151.  
  152.     /* trash the updated mail folder if it is empty */
  153.     if ((stat(folder_pathname,&folder_stat) == 0) && (folder_stat.st_size == 0))
  154.         unlink(folder_pathname);
  155.  
  156.     fclose(scb->wf);
  157.     scb->wf = NULL;
  158.     xfree((char *)scb->msg_status);
  159.     scb->msg_status = NULL;
  160. }
  161.  
  162. static void near
  163. do_cleanup(scb)
  164. struct pop_scb *scb;
  165. {
  166.     close_folder(scb);
  167.     scb->state = DONE;
  168. }
  169.  
  170. static void near
  171. deletemsg(scb,msg_no)
  172. struct pop_scb *scb;
  173. int msg_no;
  174. {
  175.     unsigned int mask = 1,offset;
  176.  
  177.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  178.         return;
  179.     msg_no--;
  180.     offset = msg_no / BITS_PER_WORD;
  181.     mask <<= msg_no % BITS_PER_WORD;
  182.     scb->msg_status[offset] |= mask;
  183.     scb->folder_modified = TRUE;
  184. }
  185.  
  186. static void near
  187. print_message_length(scb)
  188. struct pop_scb *scb;
  189. {
  190.     char *print_control_string;
  191.  
  192.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  193.         return;
  194.     if (scb->msg_len > 0 || scb->msg_num <= scb->folder_len)
  195.         print_control_string = length_rsp;
  196.     else
  197.         print_control_string = no_more_rsp;
  198.  
  199.     usprintf(scb->socket,print_control_string,scb->msg_len,scb->msg_num);
  200. }
  201.  
  202. static void near
  203. get_message(scb,msg_no)
  204. struct pop_scb    *scb;
  205. int msg_no;
  206. {
  207.     char line[BUF_LEN], *cp;
  208.  
  209.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  210.         return;
  211.  
  212.     scb->msg_len = 0;
  213.  
  214.     if (msg_no > scb->folder_len) {
  215.         scb->curpos  = 0;
  216.         scb->nextpos = 0;
  217.         return;
  218.     } else {
  219.         /* find the message and its length */
  220.         rewind(scb->wf);
  221.         while (!feof(scb->wf) && (msg_no > -1)) {
  222.             if (msg_no > 0)
  223.                 scb->curpos = ftell(scb->wf);
  224.  
  225.             fgets(line,BUF_LEN,scb->wf);
  226.             if((cp = strpbrk(line,"\r\n")) != NULLCHAR)
  227.                 *cp = '\0';
  228.  
  229.             if (isSOM(line))
  230.                 msg_no--;
  231.  
  232.             if (msg_no != 0)
  233.                 continue;
  234.  
  235.             scb->nextpos  = ftell(scb->wf);
  236.             scb->msg_len += (strlen(line)+2);    /* Add CRLF */
  237.         }
  238.     }
  239.  
  240.     if (scb->msg_len > 0)
  241.         fseek(scb->wf,scb->curpos,SEEK_SET);
  242.  
  243.     /* we need the pointers even if the message was deleted */
  244.     if(isdeleted(scb,scb->msg_num))
  245.         scb->msg_len = 0;
  246. }
  247.  
  248. static void near
  249. read_message(scb)
  250. struct pop_scb    *scb;
  251. {
  252.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  253.         return;
  254.  
  255.     if (scb->buf[sizeof(read_cmd) - 1] == ' ')
  256.         scb->msg_num = atoi(&(scb->buf[sizeof(read_cmd) - 1]));
  257.     else
  258.         scb->msg_num++;
  259.  
  260.     get_message(scb,scb->msg_num);
  261.     print_message_length(scb);
  262.     scb->state  = ITEM;
  263. }
  264.  
  265. static void near
  266. retrieve_message(scb)
  267. struct pop_scb    *scb;
  268. {
  269.     char line[BUF_LEN], *cp;
  270.     long cnt;
  271.  
  272.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  273.         return;
  274.  
  275.     if (scb->msg_len == 0) {
  276.         state_error(scb,"Msg already deleted");
  277.         return;
  278.     }
  279.     cnt  = scb->msg_len;
  280.  
  281.     while(!feof(scb->wf) && (cnt > 0)) {
  282.         fgets(line,BUF_LEN,scb->wf);
  283.         if((cp = strpbrk(line,"\r\n")) != NULLCHAR)
  284.             *cp = '\0';
  285.  
  286.         usprintf(scb->socket,msg_line,line);
  287.         cnt -= (strlen(line)+2);    /* Compensate for CRLF */
  288.     }
  289.     scb->state = NEXT;
  290. }
  291.  
  292. static void near
  293. open_folder(scb)
  294. struct pop_scb    *scb;
  295. {
  296.     char folder_pathname[64], line[BUF_LEN];
  297.     FILE *fd;
  298.     struct stat folder_stat;
  299.  
  300.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  301.     scb->folder_len = 0;
  302.     scb->folder_file_size = 0;
  303.  
  304.     if (stat(folder_pathname,&folder_stat)){
  305.          usputs(scb->socket,no_mail_rsp);
  306.          return;
  307.     }
  308.     scb->folder_file_size = folder_stat.st_size;
  309.     if ((fd = open_file(folder_pathname,"r",0,1)) == NULLFILE)
  310.         return;
  311.  
  312.     if ((scb->wf = temp_file(0,1)) == NULLFILE) {
  313.         fclose(fd);
  314.         return;
  315.     }
  316.     while(!feof(fd)) {
  317.         fgets(line,BUF_LEN,fd);
  318.  
  319.         /* scan for begining of a message */
  320.         if (isSOM(line))
  321.             scb->folder_len++;
  322.  
  323.         /* now put  the line in the work file */
  324.         fputs(line,scb->wf);
  325.     }
  326.     fclose(fd);
  327.  
  328.     scb->msg_status_size = (scb->folder_len) / BITS_PER_WORD;
  329.  
  330.     if ((((scb->folder_len) % BITS_PER_WORD) != 0)
  331.       || (scb->msg_status_size == 0))
  332.         scb->msg_status_size++;
  333.  
  334.     if ((scb->msg_status = (unsigned int *)cxallocw(scb->msg_status_size,
  335.                 sizeof(unsigned int))) == NULL) {
  336.         state_error(scb,"Unable to create message status array");
  337.         return;
  338.     }
  339.  
  340.     usprintf(scb->socket,count_rsp,scb->folder_len);
  341.  
  342.     scb->state  = MBOX;
  343. }
  344.  
  345. #ifdef POP_FOLDERS
  346. static void near
  347. select_folder(scb)
  348. struct pop_scb    *scb;
  349. {
  350.     char *cp = scb->buf;
  351.  
  352.     while(*cp++ != ' ') ;
  353.     strcpy(scb->username,cp);
  354.  
  355.     if (scb->wf != NULL)
  356.         close_folder(scb);
  357.     open_folder(scb);
  358. }
  359. #endif
  360.  
  361. static int near
  362. poplogin(username,pass)
  363. char *pass;
  364. char *username;
  365. {
  366.     char buf[BUF_LEN], *cp, *cp1;
  367.     FILE *fp;
  368.  
  369.     if((fp = fopen(Popusers,READ_TEXT)) == NULLFILE) {
  370.         /* User file doesn't exist */
  371.         return(FALSE);
  372.     }
  373.  
  374.     while(fgets(buf,BUF_LEN,fp),!feof(fp)) {
  375.         if(buf[0] == '#')
  376.             continue;    /* Comment */
  377.  
  378.         if((cp = strchr(buf,':')) == NULLCHAR)
  379.             /* Bogus entry */
  380.             continue;
  381.  
  382.         *cp++ = '\0';        /* Now points to password */
  383.         if(strcmp(username,buf) == 0)
  384.             break;        /* Found user name */
  385.     }
  386.  
  387.     if(feof(fp)) {
  388.         /* User name not found in file */
  389.         fclose(fp);
  390.         return(FALSE);
  391.     }
  392.     fclose(fp);
  393.  
  394.     if ((cp1 = strchr(cp,':')) == NULLCHAR)
  395.         return(FALSE);
  396.  
  397.     *cp1 = '\0';
  398.     if(strcmp(cp,pass) != 0) {
  399.         /* Password required, but wrong one given */
  400.  
  401.         return(FALSE);
  402.     }
  403.     /* whew! finally made it!! */
  404.     return(TRUE);
  405. }
  406.  
  407. static void near
  408. pop_sm(scb)
  409. struct pop_scb *scb;
  410. {
  411.     if (scb == NULLSCB)    /* be certain it is good -- wa6smn */
  412.         return;
  413.  
  414.     switch(scb->state) {
  415.     case AUTH:
  416.         if (strncmp(scb->buf,login_cmd,strlen(login_cmd)-2) == 0) {
  417.             char password[40], *cp1, *cp = scb->buf;
  418.             while(*cp++ != ' ') ;
  419.             cp1 = cp;
  420.             while(*++cp != ' ') ;
  421.             *cp = '\0';
  422.             strcpy(scb->username,cp1);
  423.             strcpy(password,++cp);
  424.             if (!poplogin(scb->username,password)) {
  425. #ifdef LOG
  426.                 log(scb->socket,"POP  access DENIED to %s",scb->username);
  427. #endif
  428.                 state_error(scb,"Access denied");
  429.                 return;
  430.             }
  431. #ifdef LOG
  432.             log(scb->socket,"POP  access granted to %s",scb->username);
  433. #endif
  434.             open_folder(scb);
  435.         } else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0){
  436.             do_cleanup(scb);
  437.         } else
  438.             state_error(scb,"(AUTH) Expected HELO or QUIT command");
  439.         break;
  440.  
  441.     case MBOX:
  442.         if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
  443.             read_message(scb);
  444.  
  445. #ifdef POP_FOLDERS
  446.         else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
  447.             select_folder(scb);
  448. #endif
  449.         else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0) {
  450.             do_cleanup(scb);
  451.         } else
  452.             state_error(scb,
  453. #ifdef POP_FOLDERS
  454.                     "(MBOX) Expected FOLD, READ, or QUIT command");
  455. #else
  456.                     "(MBOX) Expected READ or QUIT command");
  457. #endif
  458.         break;
  459.  
  460.     case ITEM:
  461.         if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
  462.             read_message(scb);
  463. #ifdef POP_FOLDERS
  464.         else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
  465.             select_folder(scb);
  466. #endif
  467.         else if (strncmp(scb->buf,retr_cmd,strlen(retr_cmd)) == 0)
  468.             retrieve_message(scb);
  469.         else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0)
  470.             do_cleanup(scb);
  471.         else
  472.             state_error(scb,
  473. #ifdef POP_FOLDERS
  474.                "(ITEM) Expected FOLD, READ, RETR, or QUIT command");
  475. #else
  476.                "(ITEM) Expected READ, RETR, or QUIT command");
  477. #endif
  478.         break;
  479.  
  480.     case NEXT:
  481.         if (strncmp(scb->buf,ackd_cmd,strlen(ackd_cmd)) == 0){
  482.             /* ACKD processing */
  483.             deletemsg(scb,scb->msg_num);
  484.             scb->msg_num++;
  485.             get_message(scb,scb->msg_num);
  486.         } else if (strncmp(scb->buf,acks_cmd,strlen(acks_cmd)) == 0){
  487.             /* ACKS processing */
  488.             scb->msg_num++;
  489.             get_message(scb,scb->msg_num);
  490.         } else if (strncmp(scb->buf,nack_cmd,strlen(nack_cmd)) == 0){
  491.             /* NACK processing */
  492.             fseek(scb->wf,scb->curpos,SEEK_SET);
  493.         } else {
  494.             state_error(scb,"(NEXT) Expected ACKD, ACKS, or NACK command");
  495.             return;
  496.         }
  497.         print_message_length(scb);
  498.         scb->state  = ITEM;
  499.         break;
  500.  
  501.     case DONE:
  502. /*        do_cleanup(scb);    now done by the pop process */
  503.         break;
  504.  
  505.     default:
  506.         state_error(scb,"(TOP) State Error!!");
  507.         break;
  508.     }
  509. }
  510.  
  511. static void
  512. popserv(s,unused,p)
  513. int s;
  514. void *unused;
  515. void *p;
  516. {
  517.     struct pop_scb *scb;
  518.  
  519.     sockowner(s,Curproc);        /* We own it now */
  520.     log(s,"POP  open");
  521.     sockmode(s,SOCK_ASCII);
  522.  
  523.     if((scb = (struct pop_scb *)mxallocw(sizeof(struct pop_scb))) == NULLSCB) {
  524.         tputs(Nospace);
  525.         close_s(s);
  526.         return;
  527.     }
  528.     scb->folder_modified = FALSE;
  529.     scb->socket = s;
  530.     scb->state  = AUTH;
  531.  
  532.     usprintf(scb->socket,greeting_msg,Hostname);
  533.  
  534. loop:
  535.     for(;;) {
  536.         if (scb->state == DONE
  537.           || (scb->count = recvline(s,scb->buf,BUF_LEN)) == -1) {
  538.             /* He closed on us */
  539.             break;
  540.         }
  541.         rip(scb->buf);
  542.         if (*scb->buf == '\0')        /* Ignore blank cmd lines */
  543.             goto loop;
  544.         pop_sm(scb);
  545.     }
  546.     do_cleanup(scb);
  547.     if(scb->count != -1)
  548.         usputs(scb->socket,signoff_msg);
  549.     log(scb->socket,"POP  close");
  550.     close_s(scb->socket);
  551.     if (scb->wf != NULL)
  552.         fclose(scb->wf);
  553.     if (scb->msg_status  != NULL)
  554.         xfree((char *)scb->msg_status);
  555.     xfree((char *)scb);
  556.  
  557. }
  558.  
  559.  
  560.  
  561. /* ---------------------- POP start/stop cmds ---------------------- */
  562.  
  563. /* Start up POP receiver service */
  564. int
  565. pop1(argc,argv,p)
  566. int argc;
  567. char *argv[];
  568. void *p;
  569. {
  570.     struct sockaddr_in lsocket;
  571.     int s;
  572.  
  573.     if (Spop != -1)
  574.         return 0;
  575.  
  576.     psignal(Curproc,0);        /* Don't keep the parser waiting */
  577.     chname(Curproc,"POP listener");
  578.  
  579.     lsocket.sin_family = AF_INET;
  580.     lsocket.sin_addr.s_addr = INADDR_ANY;
  581.     lsocket.sin_port = (argc < 2) ? IPPORT_POP : atoi(argv[1]);
  582.  
  583.     Spop = socket(AF_INET,SOCK_STREAM,0);
  584.     bind(Spop,(char *)&lsocket,sizeof(lsocket));
  585.     listen(Spop,1);
  586.  
  587.     for (;;) {
  588.         if((s = accept(Spop,NULLCHAR,(int *)NULL)) == -1)
  589.             break;    /* Service is shutting down */
  590.  
  591.         if(availmem() < Memthresh){
  592.             usputs(s,Nospace);
  593.             shutdown(s,1);
  594.         } else {
  595.             sockmode(s,SOCK_ASCII);
  596.             /* Spawn a server */
  597.             newproc("POP server",1536,popserv,s,NULL,NULL,0);
  598.         }
  599.     }
  600.     return 0;
  601. }
  602.  
  603. /* Shutdown POP service (existing connections are allowed to finish) */
  604. int
  605. pop0(argc,argv,p)
  606. int argc;
  607. char *argv[];
  608. void *p;
  609. {
  610.     close_s(Spop);
  611.     Spop = -1;
  612.     return 0;
  613. }
  614.  
  615. #endif /* POP */